﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
using System.Text.RegularExpressions;
using Fynd.Hermes.Web.Objects;
using FyndSharp.Data;
using FyndSharp.Data.Orm;
using System.Data.Common;
using FyndSharp.Utilities.Common;
using System.Data;

namespace Fynd.Hermes.Web.Pages
{
    public partial class ManagedFileUpload : RoleBasedPage
    {
        protected override IEnumerable<UserRole> AccreditedRoles
        {
            get
            {
                return new UserRole[] { UserRole.NormalUser, UserRole.Administrator };
            }
        }
        private static readonly string _ApkInvalidMessage = "Hello，上传的文件虽然看起来像APK文件，实际上不是APK文件。";

        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void ButtonUpload_Click(object sender, EventArgs e)
        {
            // 检查文件名
            string fileExtensionName = Path.GetExtension(this.FileUpload1.FileName);
            if (fileExtensionName.Equals(".apk", StringComparison.OrdinalIgnoreCase) == false)
            {
                this.LabelMessage.Text = "额，上传的文件看起来都不像APK文件哦？";
                return;
            }


            
            bool isMainfestFileChecked = false;
            bool isClassesDexChecked = false;
            bool isResourcesArscChecked = false;
            bool hasSignatureSF = false;
            bool hasSignatureRsa = false;

            MemoryStream uploadedCopyMemory = null;

            try
            {

                uploadedCopyMemory = new MemoryStream(this.FileUpload1.FileBytes);
                using (ZipInputStream theZip = new ZipInputStream(uploadedCopyMemory))
                {
                    ZipEntry theEntry;
                    while ((theEntry = theZip.GetNextEntry()) != null)
                    {
                        if (theEntry.IsFile)
                        {
                            if (theEntry.Name.Equals("AndroidManifest.xml"))
                            {
                                // 检查AndroidManifest.xml文件头4个字节
                                byte[] data = new byte[4];
                                int size = theZip.Read(data, 0, 4);
                                if (size >= 4 && data[0] == 0x03 && data[1] == 0x00 && data[2] == 0x08 && data[3] == 0x00)
                                {
                                    isMainfestFileChecked = true;
                                }
                                else
                                {
                                    isMainfestFileChecked = false;
                                }
                            }
                            else if (theEntry.Name.Equals("classes.dex"))
                            {
                                // 检查classes.dex文件头8个字节
                                byte[] data = new byte[8];
                                int size = theZip.Read(data, 0, 8);
                                if (size >= 8
                                    && data[0] == 0x64
                                    && data[1] == 0x65
                                    && data[2] == 0x78
                                    && data[3] == 0x0a
                                    && data[4] == 0x30
                                    && data[5] == 0x33
                                    && data[6] == 0x35
                                    && data[7] == 0x00)
                                {

                                    isClassesDexChecked = true;
                                }
                                else
                                {
                                    isClassesDexChecked = false;
                                }
                            }
                            else if (theEntry.Name.Equals("resources.arsc"))
                            {
                                // 检查resources.arsc文件头4个字节
                                byte[] data = new byte[4];
                                int size = theZip.Read(data, 0, 4);
                                if (size >= 4 && data[0] == 0x02 && data[1] == 0x00 && data[2] == 0x0c && data[3] == 0x00)
                                {
                                    isResourcesArscChecked = true;
                                }
                                else
                                {
                                    isResourcesArscChecked = false;
                                }
                            }
                            else if (theEntry.Name.IndexOf("META-INF/") == 0)
                            {

                                if (Regex.IsMatch(theEntry.Name, "\\.SF$", RegexOptions.Multiline))
                                {
                                    hasSignatureSF = true;
                                }
                                if (Regex.IsMatch(theEntry.Name, "\\.RSA$", RegexOptions.Multiline))
                                {
                                    hasSignatureRsa = true;
                                }
                            }
                        } // end of if zip entry is file

                        // All pass! break loop.
                        if (isMainfestFileChecked && isClassesDexChecked && isResourcesArscChecked && hasSignatureRsa && hasSignatureSF)
                        {
                            break;
                        }
                    } // end of while zip entry
                } // end of zip stream using
            }
            catch
            {
                this.LabelMessage.Text = _ApkInvalidMessage;
                return;
            }
            finally
            {
                if (null != uploadedCopyMemory)
                {
                    uploadedCopyMemory.Close();
                }
            }

            if (isMainfestFileChecked && isClassesDexChecked && isResourcesArscChecked && hasSignatureRsa && hasSignatureSF)
            {
                DateTime now = DateTime.Now;
                string fileName = Path.GetFileName(this.FileUpload1.FileName);
                string relativePath = now.Year.ToString() + "/" + now.Month.ToString() + "/" + now.Day.ToString() + "/" + fileName;
                string absPath = (Global.UploadRootPath.StartsWith("~/") 
                        ? Server.MapPath(Global.UploadRootPath) 
                        : Global.UploadRootPath)  
                    + relativePath;
                // 检查文件是否已存在
                if (File.Exists(absPath))
                {
                    if (CheckManagedFileDbRecordExists(relativePath) == false)
                    {
                        // 文件在硬盘上存在，但是数据库无记录，删除硬盘上的文件
                        File.Delete(absPath);
                    }
                    else
                    {
                        this.LabelMessage.Text = fileName + "已经存在了，换个文件名试试。";
                        return;
                    }
                }

                string absDir = Path.GetDirectoryName(absPath);
                // 目录不存在时创建目录
                if (Directory.Exists(absDir) == false)
                {
                    Directory.CreateDirectory(absDir);
                }

                // 保存文件
                this.FileUpload1.FileContent.Seek(0, 0);
                this.FileUpload1.SaveAs(absPath);

                // 保存文件记录到数据库
                UserInfomation theUser = Context.Items[Global.HttpContextKeyUser] as UserInfomation;
                ManagedFile theFile = new ManagedFile()
                {
                    CreatedBy = theUser.LoginName,
                    CreatedDate = DateTime.Now,
                    ModifiedBy = theUser.LoginName,
                    ModifiedDate = DateTime.Now,
                    RelativePath = relativePath,
                    UploaderTrueName = theUser.TrueName
                };

                OperationLog log = new OperationLog()
                {
                    CreatedBy = theUser.LoginName,
                    CreatedDate = now,
                    ModifiedBy = theUser.LoginName,
                    ModifiedDate = now,
                    Message = String.Format("{0}({1})于{2}上传了文件{3}到{4}，IP：{5}"
                        , theUser.TrueName
                        , theUser.EmployeeNumber
                        , now.ToString()
                        , fileName
                        , relativePath
                        , Request.UserHostAddress),
                    OperationDate = now,
                    ClientIP = Request.UserHostAddress,
                    UserLoginName = theUser.LoginName,
                    OperatorTrueName = theUser.TrueName
                };

                AdoSession db = AdoSessionManager.Current["hermes"];
                bool isDbOpen = false;
                bool isTransBegin = false;
                try
                {
                    isDbOpen = db.Open();
                    isTransBegin = db.BeginTransaction();

                    OrmObjectFactory ormFactory = new OrmObjectFactory(delegate()
                    {
                        return Guid.NewGuid().ToString();
                    }, db);
                    DbCommand cmd = ormFactory.CreateSaveDbCommand(theFile);
                    db.ExecuteNonQuery(cmd);

                    DbCommand logCmd = ormFactory.CreateSaveDbCommand(log);
                    db.ExecuteNonQuery(logCmd);

                    if (isTransBegin)
                    {
                        db.Commit();
                    }
                    
                }
                catch
                {
                    if (isTransBegin)
                    {
                        db.Rollback();
                    }
                    // 发生数据库异常时删除已经保存的文件
                    try
                    {
                        File.Delete(absPath);
                    }
                    catch
                    {
                        // UNDONE 忽略删除失败，下次上传相同文件名的文件时会自动修复，使数据库记录和硬盘上的文件保持一致。
                    }
                    throw;
                }
                finally
                {
                    if (isDbOpen)
                    {
                        db.Close();
                    }
                }

                this.LabelMessage.Text = "嗯，上传成功！";
                Response.Redirect("ManagedFileList.aspx");
            }
            else
            {
                this.LabelMessage.Text = _ApkInvalidMessage;
                return;
            }
        }

        private bool CheckManagedFileDbRecordExists(string relativePath)
        {
            AdoSession db = AdoSessionManager.Current["hermes"];
            bool isDbOpen = false;
            try
            {
                isDbOpen = db.Open();

                using(DbCommand cmd = db.CreateCommand("SELECT 1 FROM managed_file WHERE relative_path=@relative_path"))
                {
                    cmd.Parameters.Add(db.CreateParameter("@relative_path", DbType.String, relativePath));
                    return TypeConvert.ToBool(db.ExecuteScalar(cmd));
                }
            }
            finally
            {
                if (isDbOpen)
                {
                    db.Close();
                }
            }
        }

    }
}